/*LICENSE_BEGIN
 * ******************************************************************
 * Copyright 2006 by the Dainamite Robocup-Team at the
 * DAI-Labor, TU-Berlin (www.dai-labor.de)
 * 
 * Members of Dainamite are:
 * Holger Endert
 * Robert Wetzker
 * Thomas Karbe
 * Axel Heßler
 * Philippe Bütter
 * Felix Brossmann
 * 
 * Licensed under the GNU GPL (General Public License) Free Software 
 * License, Version 2. You may use this file only in compliance with 
 * the License. You may obtain a copy of the License at:
 * 
 * http://www.fsf.org/licensing/licenses/gpl.txt
 * ******************************************************************
LICENSE_END*/
package robocup.component.situations;

import robocup.component.Prophet;
import robocup.component.WorldModel;
import robocup.component.actions.Action;
import robocup.component.worldobjects.Player;

/**
 * The abstract <code>Situation</code> class. Things needed in all Situations 
 * are defined here.<br>
 * 
 * Using situations we try to describe and classify the world on a higher level.
 * A situation is suitable only for certain worlds (i.e. the PassSituation 
 * matches only worlds where we have the ball control) and there may be more 
 * than one situation classifying a given world (i.e. Passing and Dribbling). If 
 * a situation occurs each child instance of the <code>Situation</code> class
 * will try to find out what action fist best this situation and what benefit 
 * would result from performing this action. The benefits and actions will
 * later be used by the tactic on the state-level to determine the best state.
 * A situation also creates new knowledge from the worldmodel facts we know. 
 * It's calculated(updated) only once when needed!<br>
 * The evaluation of all situations is done in the {@link SituationEvaluator}.
 * <br><br>
 *
 * The devision between states and situations is not very strict as situations
 * already perform the benefit and bestAction calculations keeping the states 
 * themself pretty simple. For a better understanding you might think of a 2 
 * layered approach with the Situations doing most of the resource intensive 
 * calculations and the states used for choosing from a pool of situations the
 * best resulting action.
 */
public abstract class Situation {

	private static String EMPTY_STRING = "";

  /**
   * the {@link #subStateString} is used to inform the gui what part of the
   * situation's decision tree was chosen.
   */
  protected String subStateString = EMPTY_STRING;
  
	private WorldModel world;

	private Prophet prophet;

	private Movements movements;

	private Player[] allPlayers;

  /**
   * as each situation is calcualted only once for a given world we remember the 
   * last update time. as in 1 cycle the world may be update only 2x (after BSI 
   * and VI arrival) each situation is at most calculated 2x a cycle (SayInfos 
   * will NOT trigger a new calculation process but will be considered in the 
   * VI process as they arrive previous to VIs).
   */
	private int timestamp = -1;

  /**
   * the benefit for this situation. 0 when the situation does not fit the 
   * world. 
   */
	protected double benefit = 0;

  /**
   * the best action resulting from the situation. "null" if the situation does 
   * not fit the world.
   */
	protected Action bestAction = null;


	/**
	 * The super-class of all situations, providing some methods that should be
	 * reused in order to fit to the framework.
	 * 
	 * @param world -
	 *            the current world-model
	 * @param movements -
	 *            the generic movement-model
	 */
	public Situation(WorldModel world, Movements movements) {

		this.world = world;
		this.movements = movements;
		this.allPlayers = world.getAllPlayers();
	}

	/**
	 * The super-class of all situations, providing some methods that should be
	 * reused in order to fit to the framework.
	 * 
	 * @param world -
	 *            the current world-model
	 * @param prophet -
	 *            the parent object, holding all situations
	 * @param movements -
	 *            the generic movement-model
	 */
	public Situation(WorldModel world, Prophet prophet, Movements movements) {

		this.world = world;
		this.prophet = prophet;
		this.movements = movements;
		this.allPlayers = world.getAllPlayers();
	}

	/**
	 * updates the situation
	 * 
	 */
	protected void update() {
		this.setUpdated();
	}

	/**
	 * sets a timestamp. so the situation doesn't have to be recalculated if the
	 * world didn't change.
	 * 
	 */
	protected void setUpdated() {

		this.timestamp = this.world.getBodyCycle() * 10
				+ (this.world.isViBased() ? 1 : 0);
	}

	/**
	 * the situation was already calculated based on the given world.
	 * 
	 * @return true if calculated
	 */
	protected boolean wasUpdated() {

		boolean ret = this.timestamp == this.world.getBodyCycle() * 10
				+ (this.world.isViBased() ? 1 : 0);

		if (!ret) {
			subStateString = EMPTY_STRING;
		}
		return ret;
	}

	/**
	 * the situations benefit.
	 * 
	 * @return
	 */
	public double getBenefit() {

		if (!this.wasUpdated())
			this.update();

		return this.benefit;
	}

	/**
	 * the action resulting in the best benefit.
	 * 
	 * @return the best action
	 */
	public Action getBestAction() {

		if (!this.wasUpdated())
			this.update();

		return this.bestAction;
	}

	/**
	 * @return the subStateString showing which way in the decision tree of the
   *            Situation was chosen. (To be shown in the GUI.)
	 */
	public String getSubStateString() {

		if (!this.wasUpdated())
			this.update();

		return subStateString;
	}

	/**
	 * sets the substate
	 * 
	 * @param s -
	 *            the subStateString showing which way in the decision tree of the
   *            Situation was chosen. (To be shown in the GUI.)
	 */
	public void setSubStateString(String s) {
		subStateString = s;
	}

	/**
	 * @return Returns the allPlayers.
	 */
	public Player[] getAllPlayers() {

		return this.allPlayers;
	}

	/**
	 * @return Returns the movements.
	 */
	public Movements getMovements() {

		return this.movements;
	}

	/**
	 * @return Returns the world.
	 */
	public WorldModel getWorld() {

		return this.world;
	}

	/**
	 * @param benefit -
	 *            The benefit of the current situation when choosing the 
   *            <code>{@link #bestAction}</code>.
	 */
	public void setBenefit(double benefit) {

		this.benefit = benefit;
	}

	/**
	 * @param bestAction
	 *            The bestAction in the current situation.
	 */
	public void setBestAction(Action bestAction) {

		this.bestAction = bestAction;
	}

	/**
	 * @return Returns the prophet.
	 */
	public Prophet getProphet() {

		return this.prophet;
	}

	/**
	 * @return Returns the timestamp.
	 */
	public int getTimestamp() {

		return this.timestamp;
	}
}
